Replacing key.jwk in Multipaz Servers
Before You Begin
This tutorial guides you through replacing the key.jwk file used for authentication between the Multipaz OpenID4VCI Server and the Records Server. The key is used to sign and verify JWT client assertions that authenticate the OpenID4VCI server when communicating with the Records server.
Prerequisites
- Access to the Multipaz repository
- Basic understanding of JSON Web Keys (JWK)
- Text editor
- Terminal/command line access
- Understanding of the Multipaz server architecture
- A new EC P-256 key pair in JWK format (or knowledge of how to generate one)
What You'll Learn
- What
key.jwkis and how it's used in the system - Where the key needs to be configured in both servers
- Step-by-step instructions for replacing the key
- How to verify the replacement was successful
- Security best practices for key management
Understanding key.jwk
The key.jwk file contains a JSON Web Key (JWK) with an Elliptic Curve P-256 key pair. This key is used for:
- OpenID4VCI Server: Signs JWT client assertions when communicating with the Records Server
- Records Server: Verifies JWT client assertions from the OpenID4VCI Server
The key contains:
- Public key components:
x,y,crv(curve) - Private key component:
d(used only by the OpenID4VCI Server) - Key identifier:
kid(used to look up the public key)
Files That Need Updating
When replacing key.jwk, you need to update three locations:
key.jwk(root directory) - The key file itselfmultipaz-openid4vci-server/src/main/resources/resources/default_configuration.json-system_of_record_jwksectionmultipaz-records-server/src/main/resources/resources/default_configuration.json-trusted_client_assertionssection
⚠️ Important: The kid (key ID) must match exactly in both server configurations.
Step-by-Step Replacement Guide
Step 1: Prepare Your New key.jwk
Create or obtain a new EC P-256 key pair in JWK format. Your new key should have this structure:
{
"kty": "EC",
"alg": "ES256",
"kid": "your-unique-key-id-here",
"crv": "P-256",
"x": "base64url-encoded-x-coordinate",
"y": "base64url-encoded-y-coordinate",
"d": "base64url-encoded-private-key"
}
Field Explanations
Each field in the key.jwk file has a specific purpose:
-
"kty": "EC"- Key Type: Specifies that this is an Elliptic Curve key pair
- Value: Must be
"EC"for Elliptic Curve cryptography - Purpose: Identifies the cryptographic algorithm family used
-
"alg": "ES256"- Algorithm: Specifies the signature algorithm used
- Value:
"ES256"means ECDSA (Elliptic Curve Digital Signature Algorithm) using P-256 curve and SHA-256 hash - Purpose: Defines how signatures are created and verified
- Note: This must match in both server configurations
-
"kid": "your-unique-key-id-here"- Key ID: A unique identifier for this key
- Value: A string that uniquely identifies this key (e.g., UUID like
"bdbb8887-7cb7-4457-8a3f-1216924ed543") - Purpose: Used by the Records Server to look up the corresponding public key in
trusted_client_assertions - Critical: This value must match exactly in both server configurations
-
"crv": "P-256"- Curve: Specifies the elliptic curve used
- Value:
"P-256"refers to the secp256r1 curve (also known as NIST P-256) - Purpose: Defines the mathematical curve parameters for the key pair
- Note: This determines the size and format of the
x,y, anddvalues
-
"x": "base64url-encoded-x-coordinate"- X Coordinate: The X coordinate of the public key point on the elliptic curve
- Value: Base64URL-encoded bytes representing the X coordinate
- Purpose: Part of the public key (along with
yandcrv) - Example:
"tvNovlSdvTIjUW4okuSmeMiM7egvRLaj8W45MVXEM8Y"
-
"y": "base64url-encoded-y-coordinate"- Y Coordinate: The Y coordinate of the public key point on the elliptic curve
- Value: Base64URL-encoded bytes representing the Y coordinate
- Purpose: Part of the public key (along with
xandcrv) - Note: Together with
x, this defines the complete public key point - Example:
"L0AQ7DU9f6kyYdrJZlCfD0LjhoXQtX7lmfubjjIdLCg"
-
"d": "base64url-encoded-private-key"- Private Key: The secret scalar used for signing
- Value: Base64URL-encoded bytes representing the private key
- Purpose: Used by the OpenID4VCI Server to sign JWT client assertions
- Security: ⚠️ CRITICAL - This is a secret value that must be kept confidential
- Note: This field is only included in the OpenID4VCI server configuration, not in the Records server
- Example:
"lYmbuSV3m7XwvDfU8xAkWRlNUPd_lWGXMEtslR_fFxI"
Important Notes:
- The
kidmust be unique and consistent across both servers - Use a secure method to generate the key pair
- Keep the private key (
d) secure and never expose it publicly - The public key (
x,y,crv) can be shared, but the private key (d) must remain secret
Step 2: Replace key.jwk File
Replace the root key.jwk file with your new key:
# Backup the old key (recommended)
cp key.jwk key.jwk.backup
# Replace with your new key
# (Copy your new key.jwk content to the file)
File location: /path/to/identity-credential/key.jwk
Step 3: Update OpenID4VCI Server Configuration
Update the OpenID4VCI server configuration file:
File: multipaz-openid4vci-server/src/main/resources/resources/default_configuration.json
Find the system_of_record_jwk section (around line 45) and replace it with your new key:
"system_of_record_jwk": {
"kty": "EC",
"alg": "ES256",
"kid": "your-new-key-id", // ⚠️ Must match kid from your new key.jwk
"crv": "P-256",
"x": "your-new-x-value", // From your new key.jwk
"y": "your-new-y-value", // From your new key.jwk
"d": "your-new-d-value" // ⚠️ Private key - must be included here
}
Important Notes:
- Include all fields, including the private key
d - The
kidmust match exactly in both server configurations - This server needs the private key to sign JWTs
Example:
"system_of_record_jwk": {
"kty": "EC",
"alg": "ES256",
"kid": "bdbb8887-7cb7-4457-8a3f-1216924ed543",
"crv": "P-256",
"x": "tvNovlSdvTIjUW4okuSmeMiM7egvRLaj8W45MVXEM8Y",
"y": "L0AQ7DU9f6kyYdrJZlCfD0LjhoXQtX7lmfubjjIdLCg",
"d": "lYmbuSV3m7XwvDfU8xAkWRlNUPd_lWGXMEtslR_fFxI"
}
Step 4: Update Records Server Configuration
Update the Records server configuration file:
File: multipaz-records-server/src/main/resources/resources/default_configuration.json
Find the trusted_client_assertions section (around line 5) and update the entry:
"trusted_client_assertions": {
"your-new-key-id": { // ⚠️ Key must be the kid value (same as in openid4vci server)
"kty": "EC",
"alg": "ES256",
"crv": "P-256",
"x": "your-new-x-value", // From your new key.jwk
"y": "your-new-y-value" // From your new key.jwk
// ⚠️ NO "d" field here - this is public key only!
}
}
Important Notes:
- The object key must be the
kidvalue (e.g.,"bdbb8887-7cb7-4457-8a3f-1216924ed543") - Do NOT include the
dfield - this server only verifies signatures - If replacing an existing key, remove the old entry or update it
Example:
"trusted_client_assertions": {
"bdbb8887-7cb7-4457-8a3f-1216924ed543": {
"kty": "EC",
"alg": "ES256",
"crv": "P-256",
"x": "tvNovlSdvTIjUW4okuSmeMiM7egvRLaj8W45MVXEM8Y",
"y": "L0AQ7DU9f6kyYdrJZlCfD0LjhoXQtX7lmfubjjIdLCg"
}
}
Step 5: Rebuild and Redeploy Servers
After updating the configurations, you need to rebuild and redeploy both servers.
For Local Development:
# Rebuild the OpenID4VCI server
./gradlew :multipaz-openid4vci-server:buildFatJar
# Rebuild the Records server
./gradlew :multipaz-records-server:buildFatJar
For Cloud Run Deployment:
- Rebuild the fat JARs (as shown above)
- Rebuild Docker images
- Redeploy to Cloud Run
Refer to the Deploying to Google Cloud Run tutorial for detailed deployment steps.
Verification Checklist
After replacing the key, verify the following:
-
key.jwkcontains the new key with all fields (kty,alg,kid,crv,x,y,d) - OpenID4VCI server
system_of_record_jwkhas all fields includingd - Records server
trusted_client_assertionshas an entry keyed by the newkid - Records server entry contains only public key fields (no
d) -
kidvalues match exactly in both configurations - Both servers rebuilt and redeployed successfully
Security Best Practices
-
Keep Private Key Secure
- Never commit the private key (
d) to public repositories - Use environment variables or secrets management for production deployments
- Restrict file permissions on
key.jwk
- Never commit the private key (
-
Use Unique Key IDs
- Generate a unique
kidfor each key - Use UUIDs or other unique identifiers
- Generate a unique
-
Rotate Keys Regularly
- Replace keys periodically for security
- Update both servers simultaneously during rotation
-
Backup Old Keys
- Keep backups of old keys during transition periods
- Verify new keys work before removing old ones
Troubleshooting
Issue: Authentication fails between servers
Solution:
- Verify the
kidmatches exactly in both configurations - Ensure all required fields are present
- Check that the Records server has the public key (without
d) - Verify the OpenID4VCI server has the complete key (with
d)
Issue: "CA not registered" error
Solution:
- Ensure the
kidin the JWT header matches a key intrusted_client_assertions - Verify the key entry uses the
kidas the object key - Check for typos in the
kidvalue
Issue: "Invalid JWT signature" error
Solution:
- Verify the public key (
x,y) in the Records server matches the private key used for signing - Ensure the key pair is valid (public/private key match)
- Check that the algorithm (
alg) isES256in both places
Issue: Configuration not taking effect
Solution:
- Rebuild the fat JAR files after configuration changes
- Redeploy the servers
- Clear any caches if applicable
- Verify the configuration files are included in the build
Summary
In this tutorial, you learned:
- What
key.jwkis and its role in server authentication - The three locations that need updating when replacing the key
- How to update the OpenID4VCI server configuration (with private key)
- How to update the Records server configuration (public key only)
- The importance of matching
kidvalues across configurations - Security best practices for key management
After completing these steps, your servers will use the new key for authentication. Remember to update both servers simultaneously to avoid authentication failures.
Additional Resources
Previous: Deploying to Google Cloud Run